package juego;

import java.util.Observable;

import javax.xml.parsers.ParserConfigurationException;

import mapa.Mapa;
import objetoDeMapa.CambioDeVehiculo;
import objetoDeMapa.ControlPolicial;
import objetoDeMapa.Llegada;
import objetoDeMapa.ObjetoDeMapa;
import objetoDeMapa.Piquete;
import objetoDeMapa.Pozo;
import objetoDeMapa.SorpresaDesfavorable;
import objetoDeMapa.SorpresaFavorable;

import org.w3c.dom.Element;
import org.w3c.dom.Node;

import vehiculo.Identidad;
import vehiculo.IdentidadMoto;
import vehiculo.Vehiculo;
import aleatorio.AleatorioReal;
import aleatorio.Aleatrorio;
import celda.Celda;

public class Juego extends Observable {

	// Cinco niveles, con 8 parametros: Dimension, Pozos, Piquetes, Controles,
	// Favorables, Desfavorables, CambioVehiculo, LimiteMovimientos
	final int[][] nivel = new int[][] { { 25, 0, 0, 0, 0, 0, 0, 100 },
			{ 25, 30, 50, 30, 20, 20, 50, 100 },
			{ 28, 40, 70, 40, 25, 25, 70, 110 },
			{ 31, 50, 90, 50, 30, 30, 90, 120 },
			{ 34, 60, 110, 60, 35, 35, 110, 130 },
			{ 37, 70, 130, 70, 40, 40, 130, 140 }, };

	int nivelElegido;

	int dimensionMapa;
	int pozos;
	int piquetes;
	int controlesPoliciales;
	int sorpresasPositivas;
	int sorpresasNegativas;
	int sorpresasCambioDeVehiculo;
	int limiteMovimientos;

	private boolean estajugando;
	private int puntaje;
	public int puntajeInicial;
	private Vehiculo miVehiculo;
	private Mapa mapa;
	private int llegadaX;
	private int llegadaY;

	private Jugador jugador;

	public Juego(Identidad identidad, int puntaje, Integer nivelUsuario) {
		this.nivelElegido = nivelUsuario;
		dimensionMapa = nivel[nivelElegido][0];
		pozos = nivel[nivelElegido][1];
		piquetes = nivel[nivelElegido][2];
		controlesPoliciales = nivel[nivelElegido][3];
		sorpresasPositivas = nivel[nivelElegido][4];
		sorpresasNegativas = nivel[nivelElegido][5];
		sorpresasCambioDeVehiculo = nivel[nivelElegido][6];
		limiteMovimientos = nivel[nivelElegido][7];
		this.estajugando = true;
		this.puntajeInicial = puntaje;
		this.puntaje = puntaje;
		this.mapa = new Mapa(dimensionMapa);
		this.inicializarMapa();
		this.setVehiculo(Vehiculo.crearConIdentidad(identidad));
		this.miVehiculo.setJuego(this);
		this.miVehiculo.setMapa(mapa);
	}

	public Juego(int dimension) {
		this.mapa = new Mapa(dimension);
		this.estajugando = true;
		this.puntajeInicial = 0;
		IdentidadMoto identidad = new IdentidadMoto();
		this.miVehiculo = new Vehiculo(identidad);
	}

	public void ActualizarObservadores() {
		setChanged();
		notifyObservers();
	}

	public void finalizarJuego() {
		this.estajugando = false;
	}

	public boolean seEstaJugando() {
		return this.estajugando;
	}

	public Vehiculo getVehiculo() {
		return this.miVehiculo;
	}

	public int getPuntaje() {
		return this.puntaje;
	}

	public Jugador getJugador() {
		return this.jugador;
	}

	public int getLimite() {
		return limiteMovimientos;
	}

	public int getNivel() {
		return nivelElegido;
	}

	public void setVehiculo(Vehiculo unvehiculo) {
		this.miVehiculo = unvehiculo;
		this.miVehiculo.setPosxy(0, 3);// Supuesto 1
	}

	public void setVehiculoSerializado(Vehiculo unvehiculo) {
		this.miVehiculo = unvehiculo;
	}
	
	public void calcularPuntaje() {
		puntaje = puntajeInicial + this.miVehiculo.movimientos() * 100;
	}

	public void setearDimension(int dimension) {
		this.mapa = new Mapa(dimension);
	}

	public Mapa getMapa() {
		return this.mapa;
	}

	public void setMapa(Mapa unMapa) {
		this.mapa = unMapa;
	}

	public void actualizarVisibilidad() {
		this.mapa.actualizarVisibilidad(this.miVehiculo.getPosx(),
				this.miVehiculo.getPosy());
	}

	public void setLlegadaX(int X) {
		this.llegadaX = X;
	}

	public int getLlegadaX() {
		return this.llegadaX;
	}

	public void setLlegadaY(int Y) {
		this.llegadaY = Y;
	}

	public int getLlegadaY() {
		return this.llegadaY;
	}

	public void setLlegada(int posx, int posy) {
		this.mapa.getCeldaEnPosicion(posx, posy).setLlegada();
		this.setLlegadaX(posx);
		this.setLlegadaY(posy);
	}

	public void moverVehiculoEnUnaDireccion(Direccion direccion)
			throws NoSePuedeMoverMasJuegoFinalizado {
		try {
			if (!this.estajugando)
				throw new NoSePuedeMoverMasJuegoFinalizado();
			this.miVehiculo.moverse(direccion);
			this.calcularPuntaje();
			this.ActualizarObservadores();
		} catch (NoSePuedeMoverMasJuegoFinalizado e) {
		}
	}

	private void inicializarMapa() {

		for (int i = 0; i < controlesPoliciales; i++) {
			this.agregarAlMapa(new ControlPolicial(), new AleatorioReal(),
					new AleatorioReal());
		}

		for (int i = 0; i < pozos; i++) {
			this.agregarAlMapa(new Pozo(), new AleatorioReal(),
					new AleatorioReal());
		}

		for (int i = 0; i < piquetes; i++) {
			this.agregarAlMapa(new Piquete(), new AleatorioReal(),
					new AleatorioReal());
		}

		for (int i = 0; i < sorpresasPositivas; i++) {
			this.agregarAlMapa(new SorpresaFavorable(), new AleatorioReal(),
					new AleatorioReal());
		}

		for (int i = 0; i < sorpresasNegativas; i++) {
			this.agregarAlMapa(new SorpresaDesfavorable(), new AleatorioReal(),
					new AleatorioReal());
		}

		for (int i = 0; i < sorpresasCambioDeVehiculo; i++) {
			this.agregarAlMapa(new CambioDeVehiculo(), new AleatorioReal(),
					new AleatorioReal());
		}

		this.agregarLlegada(new AleatorioReal());
	}

	private int posicionRandomDelMapa(Aleatrorio aleatorio) {
		return (int) (Math.floor((int) (aleatorio.getNumeroAleatorio() * (mapa
				.getDimension()))));
	}

	public boolean esEsquina(int x, int y) {
		if ((x % 3 == 0) && (y % 3 == 0)) {
			return true;
		}
		return false;
	}

	public void agregarAlMapa(ObjetoDeMapa unObjeto, Aleatrorio aleatorio,
			Aleatrorio aleatorio2) {
		boolean objetoColocado = false;
		while (!objetoColocado) {
			int filaObjeto = 0;
			int columnaObjeto = 0;
			Celda celda;
			while (esEsquina(filaObjeto, columnaObjeto)) {
				filaObjeto = posicionRandomDelMapa(aleatorio);
				columnaObjeto = posicionRandomDelMapa(aleatorio2);
			}
			celda = mapa.getCeldaEnPosicion(filaObjeto, columnaObjeto);
			if (celda.estaHablilitada()) {
				mapa.agregarElementoEnPosicion(unObjeto, filaObjeto,
						columnaObjeto);
				objetoColocado = true;
			}
		}
	}

	public void agregarLlegada(Aleatrorio aleatorio) {
		boolean objetoColocado = false;
		int columnaObjeto = (mapa.getDimension() - 1);
		while (!objetoColocado) {
			Celda celda;
			int filaObjeto = posicionRandomDelMapa(aleatorio);
			while (!esEsquina(filaObjeto, columnaObjeto)) {
				filaObjeto = posicionRandomDelMapa(aleatorio);
			}
			celda = mapa.getCeldaEnPosicion(columnaObjeto, filaObjeto);
			if (celda.estaHablilitada()) {
				this.setLlegadaX(columnaObjeto);
				this.setLlegadaY(filaObjeto);
				celda.agregarObjeto(new Llegada());
				celda.cambiarVisibilidad();
				objetoColocado = true;
			}
		}

	}

	public static Juego Default(String nombreJugador, int puntaje, Integer nivel) {
		Juego unJuego = new Juego(new IdentidadMoto(), puntaje, nivel);
		Jugador unJugador = new Jugador(nombreJugador);
		unJuego.setJugador(unJugador);
		unJugador.setJuego(unJuego);
		return unJuego;

	}

	public static Juego Cargar() {
		// hay que pasarle el xml para que cargue
		return new Juego(new IdentidadMoto(), 0, 1);

	}

	public void setJugador(Jugador unJugador) {
		this.jugador = unJugador;
	}

	public Element serializar(org.w3c.dom.Document dom)
			throws ParserConfigurationException {

		Element juegoSerializado = dom.createElement("Juego");

		Element puntaje = dom.createElement("Puntaje");
		puntaje.setTextContent(((Integer) this.puntaje).toString());
		juegoSerializado.appendChild(puntaje);
		
		Element puntajeInicial = dom.createElement("PuntajeInicial");
		puntajeInicial.setTextContent(((Integer) this.puntajeInicial).toString());
		juegoSerializado.appendChild(puntajeInicial);

		Element llegadaX = dom.createElement("LlegadaX");
		llegadaX.setTextContent(((Integer) this.llegadaX).toString());
		juegoSerializado.appendChild(llegadaX);

		Element llegadaY = dom.createElement("LlegadaY");
		llegadaY.setTextContent(((Integer) this.llegadaY).toString());
		juegoSerializado.appendChild(llegadaY);

		Element nivelElegido = dom.createElement("NivelElegido");
		nivelElegido.setTextContent(((Integer) this.nivelElegido).toString());
		juegoSerializado.appendChild(nivelElegido);

		Element limiteMovimientos = dom.createElement("LimiteMovimientos");
		limiteMovimientos.setTextContent(((Integer) this.limiteMovimientos).toString());
		juegoSerializado.appendChild(limiteMovimientos);

		Element estajugando = dom.createElement("EstaJugando");
		estajugando.setTextContent(this.estajugando ? "true" : "false");
		juegoSerializado.appendChild(estajugando);

		juegoSerializado.appendChild(this.mapa.serializar(dom));
		juegoSerializado.appendChild(this.miVehiculo.serializar(dom));

		return juegoSerializado;

	}

	public static Juego deserializar(Node juego) {

		Juego juegoNuevo = Juego.Default("", 0, 0);
		try {

			for (int i = 0; i < juego.getChildNodes().getLength(); i++) {
				Node nodo = juego.getChildNodes().item(i);

				if (nodo.getNodeName() == "Mapa") {
					juegoNuevo.setMapa(Mapa.deserializar(nodo));
				} else

				if (nodo.getNodeName() == "Vehiculo") {
					juegoNuevo.setVehiculoSerializado(Vehiculo.deserializar(nodo));
				} else

				if (nodo.getNodeName() == "EstaJugando") {
					juegoNuevo.estajugando = (nodo.getTextContent().equals("true"));
				} else

				if (nodo.getNodeName() == "LlegadaX") {
					juegoNuevo.llegadaX = Integer.parseInt(nodo
							.getTextContent());
				} else

				if (nodo.getNodeName() == "LlegadaY") {
					juegoNuevo.llegadaY = Integer.parseInt(nodo
							.getTextContent());
				} else

				if (nodo.getNodeName() == "LimiteMovimientos") {
					juegoNuevo.limiteMovimientos = Integer.parseInt(nodo
							.getTextContent());
				} else

				if (nodo.getNodeName() == "NivelElegido") {
					juegoNuevo.nivelElegido = Integer.parseInt(nodo
							.getTextContent());
				} else

				if (nodo.getNodeName() == "Puntaje") {
					juegoNuevo.puntaje = Integer
							.parseInt(nodo.getTextContent());
				} else

				if (nodo.getNodeName() == "PuntajeInicial") {
					juegoNuevo.puntajeInicial = Integer.parseInt(nodo
							.getTextContent());
				}

			}
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		Vehiculo unVehiculo = juegoNuevo.getVehiculo();
		unVehiculo.setMapa(juegoNuevo.getMapa());
		unVehiculo.setJuego(juegoNuevo);

		return juegoNuevo;
	}

}
